package com.lazy.expression;

import com.lazy.annotation.Idempotent;
import com.lazy.exception.CustomRedisException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * el 表达式处理
 *
 * @author fengshaoyu
 * @date 2024/1/30 15:02
 */
public class ExpressionKeyResolver {

    private static final SpelExpressionParser PARSER = new SpelExpressionParser();
    private static final StandardReflectionParameterNameDiscoverer  DISCOVERER = new StandardReflectionParameterNameDiscoverer();

    public String resolver(Idempotent idempotent, ProceedingJoinPoint jp) {
        Method method = getMethod(jp);
        if (!StringUtils.hasText(idempotent.key())) {
            Object[] args = jp.getArgs();
            return method + Arrays.toString(args);
        } else {
            EvaluationContext evaluationContext = new StandardEvaluationContext();
            String[] parameterNames = DISCOVERER.getParameterNames(method);
            if (parameterNames != null && parameterNames.length > 0) {
                for (String parameterName : parameterNames) {
                    evaluationContext.setVariable(parameterName, parameterName);
                }
            }
            Expression expression = PARSER.parseExpression(idempotent.key());
            //解析value
            String valueKey = expression.getValue(evaluationContext, String.class);
            return method + valueKey;
        }
    }


    private Method getMethod(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        if (method.getDeclaringClass().isInterface()) {
            try {
                method = joinPoint.getTarget().getClass().getDeclaredMethod(joinPoint.getSignature().getName(),
                        method.getParameterTypes());
            }
            catch (SecurityException | NoSuchMethodException e) {
                throw new CustomRedisException("解析幂等注解异常");
            }
        }
        return method;
    }
}
